perm filename GJCRE.LSP[SCH,LSP] blob
sn#688826 filedate 1982-11-14 generic text, type T, neo UTF8
;;;-*-LISP-*-
;;; Simplest possible rubout handler, featureful yet grubby.
;;; Adapted for use under maclisp in the 6.001 Scheme System,
;;; from the NIL's bootstrap rubout handler, which
;;; was in turn adapted from GJC's hack for maclisp CGOL.
;;; 5:31am Thursday, 13 August 1981 -George Carrette.
;;; Supports: Catching of readtime errors!
;;; Rubout ... rub out last character and last read error.
;;; ↑W ....... rub out last word.
;;; ↑K ....... clear input.
;;; ↑R ....... redisplay without clear screen
;;; ↑L ....... clear screen and redisplay.
;;; ↑E ....... the calling of an external editor.
;;; ↑Y ....... yank the LAST thing read in.
;;; Note : Does not require turning off of system tty-echoing.
;;; This is featureful for TOPS-20.
;;; Exported functions:
;;; TTY-READ &optional "prompt"
;;; TTY-READLINE &optional "prompt"
;;; TTY-TYI &optional "prompt"
;;; TTY-REDISPLAY &optional "message"
;;; TTY-PRINTLINE &optional "line1" "line2" "line3" ...
(herald "Gjc-reader")
(DECLARE (*LEXPR TOPLEVEL-EDITOR-CALL ; externally defined
TTY-PRINTLINE ; forward ref.
)
(special bad-tty? echofiles-value status-ttysize))
(EVAL-WHEN (EVAL COMPILE)
(or (fboundp 'defstruct)
(load (caseq (status opsys)
((tops-20)
(OR (PROBEF '((LISP)STRUCT FASL))
"<GJC.LISP>STRUCT"))
((ITS) "LIBLSP;STRUCT")
(T '((LISP)STRUCT))))))
(defmacro errset-bind (handler &rest body)
`(let ((errset ,handler)
(undf-fnctn nil)
(unbnd-vrbl nil)
(wrng-type-arg nil)
(unseen-go-tag nil)
(wrng-no-args nil)
(fail-act nil)
(*rset-trap nil)
(pdl-overflow nil)
(io-lossage nil))
(car (errset (progn ,@body) nil))))
(defstruct (grubout-tty sfa conc-name default-pointer)
(tyi tyi)
(tyo tyo)
(prompt #'grub-prompt)
grubout untyi display to-yank kill-buffer)
(DEFVAR READ-PROMPT "> ")
(defun grub-prompt (stream) (princ read-prompt stream))
;; the display is a list ((c . (v-pos . h-pos)) (c . (v-pos . h-pos)) ...)
;; where the cursorpos is that before the character was echoed.
;; we should also be keeping the position of the cursor after echoing,
;; but we don't because it only rarely causes glitches, which can be
;; removed by typing ↑L.
(defvar scrolling? (EQ 'TOPS-20 (STATUS OPSYS)))
(defvar status-ttysize (status ttysize))
(defun grubout-tty-tyi-and-display (grubout-tty)
(let ((pos (cursorpos (grubout-tty-tyo))))
(let ((c (if (grubout-tty-to-yank)
(let ((c (pop (grubout-tty-to-yank))))
(tyo c (grubout-tty-tyo))
c)
(tyi (grubout-tty-tyi)))))
(push (cons c pos) (grubout-tty-display))
(cond ((and scrolling? (= c #\LF)
(= (car pos) (1- (car status-ttysize))))
(do ((l (grubout-tty-display) (cdr l)))
((null l))
(rplaca (cdar l) (1- (cadar l))))))
c)))
(defun re-display (grubout-tty)
(cursorpos 'a (grubout-tty-tyo))
(funcall (grubout-tty-prompt) (grubout-tty-tyo))
(re-display-sub (grubout-tty-display) (grubout-tty-tyo)
(cursorpos (grubout-tty-tyo))))
(defun re-display-sub (l s c)
(cond ((null l))
(t
(re-display-sub (cdr l) s c)
(setf (cdr (car l)) (cursorpos s))
(tyo (caar l) s))))
(defun grub-out-display (grubout-tty)
(if (grubout-tty-display)
(let (((char . cp-before) (pop (grubout-tty-display))))
(cond ((or (null cp-before)
;; patch for tops-20 maclisp lossage.
bad-tty?)
;; no cursorpos information, probably a printing terminal,
;; so call the primitive rubout function.
(rubout char (grubout-tty-tyo)))
('else
;; kill all lines between where we are and where we want
;; to be. this covers the important case of an error message
;; which was printed on the screen before we decided to
;; rubout, or any unsolicited output, say from ddt.
(do ((first t))(nil)
(let ((cp (cursorpos (grubout-tty-tyo))))
(if (= (car cp) (car cp-before)) (return nil))
(cond (first
(setq first nil)
(cond ((not (= (cdr cp) 0))
(cursorpos 'h 0 (grubout-tty-tyo))
(cursorpos 'l (grubout-tty-tyo)))))
('else
(cursorpos 'l (grubout-tty-tyo))
(cursorpos 'u (grubout-tty-tyo))))))
;; set the cursorpos back to what it was before.
(cursorpos (car cp-before) (cdr cp-before) (grubout-tty-tyo))
;; now kill the rest of that line.
(or (= char #\cr) (cursorpos 'l (grubout-tty-tyo)))))
(setf (grubout-tty-grubout) 'true))))
(defvar grubout-tty-ops ())
(defmacro define-grubout-tty-op (name l &rest body)
`(progn 'compile
(defun (,name grubout-tty-op) ,l ,@body)
(or (memq ',name grubout-tty-ops)
(push ',name grubout-tty-ops))))
(defun grubout-tty (grubout-tty com arg)
(funcall (or (get com 'grubout-tty-op)
(error "BUG: Undefined grubout-tty operation." COM 'FAIL-ACT))
grubout-tty
arg))
(define-grubout-tty-op which-operations (ignore1 ignore2)
grubout-tty-ops)
(define-grubout-tty-op tyi (grubout-tty ignore)
(if (grubout-tty-untyi)
(pop (grubout-tty-untyi))
(do ((c))(nil)
(setq c (grubout-tty-tyi-and-display grubout-tty))
(if (funcall (get-grubout-tty-command c) c grubout-tty)
(return c)))))
(define-grubout-tty-op untyi (grubout-tty arg)
(push arg (grubout-tty-untyi)))
(define-grubout-tty-op tyipeek (grubout-tty arg)
(let ((c (tyi grubout-tty arg)))
(sfa-call grubout-tty 'untyi c)
c))
(define-grubout-tty-op init (grubout-tty ignore)
(setf (grubout-tty-grubout) ())
(setf (grubout-tty-to-yank) ())
(setf (grubout-tty-kill-buffer)
(nreverse (cdr (mapcar #'car (grubout-tty-display)))))
(setf (grubout-tty-display)
(nreverse (mapcar #'list (grubout-tty-untyi))))
(re-display grubout-tty))
(define-grubout-tty-op after-read (grubout-tty reader)
(if (eq reader 'read)
(flush-untyi-whitespace grubout-tty))
(cond (echofiles-value
(terpri echofiles-value)
(funcall (grubout-tty-prompt) echofiles-value)
(echofiles-value-revout (grubout-tty-display)))))
(defun echofiles-value-revout (l)
(if l
(progn (echofiles-value-revout (cdr l))
(tyo (caar l) echofiles-value))))
(defun flush-untyi-whitespace (grubout-tty)
(do ((l (grubout-tty-untyi) (cdr l)))
((or (null l)
(not (whitespacep/.code-char (car l))))
(setf (grubout-tty-untyi) l))))
(defvar grubout-tty-plist (list 'grubout-tty-plist))
(defun get-grubout-tty-command (c)
(or (do ((l (cdr grubout-tty-plist) (cdr l)))
((null l))
(and (= (car l) c) (return (cadr l))))
#'grubout-tty-default))
(defun grubout-tty-default (ignore-c grubout-tty)
(cond ((and (grubout-tty-grubout) (grubout-tty-display))
(setf (grubout-tty-grubout) ())
(setf (grubout-tty-untyi)
(nreverse (mapcar #'car (grubout-tty-display))))
(*throw 'grubout nil))
('else 'true)))
(defmacro define-grubout-command (c argl &rest body)
(let ((name (implode (nconc (exploden "GRUBOUT-COMMAND-") (exploden c)))))
`(progn 'compile
(defun ,name ,argl ,@body)
(putprop grubout-tty-plist #',name ,c))))
(defun pop-grubout-tty-display (grubout-tty)
;; called inside naturally non-echoing characters.
(pop (grubout-tty-display)))
(define-grubout-command #\FF (ignore-c grubout-tty)
(pop-grubout-tty-display grubout-tty)
(if bad-tty?
(terpri (grubout-tty-tyo))
(cursorpos 'c (grubout-tty-tyo)))
(re-display grubout-tty)
())
(define-grubout-command #↑R (ignore-c grubout-tty)
(pop-grubout-tty-display grubout-tty)
(re-display grubout-tty)
())
(define-grubout-command #\RUBOUT (ignore-c grubout-tty)
(pop-grubout-tty-display grubout-tty)
(grub-out-display grubout-tty)
())
(define-grubout-command #↑K (ignore-c grubout-tty)
(grub-out-display grubout-tty)
(do ()
((not (grubout-tty-display)))
(grub-out-display grubout-tty))
())
(define-grubout-command #↑W (ignore-c grubout-tty)
(grub-out-display grubout-tty)
(do ()
((not (grubout-tty-display)))
(if (whitespacep/.code-char (caar (grubout-tty-display)))
(grub-out-display grubout-tty)
(return ())))
(do ()
((not (grubout-tty-display)))
(if (whitespacep/.code-char (caar (grubout-tty-display)))
(return ())
(grub-out-display grubout-tty)))
())
(define-grubout-command #↑Y (ignore-c grubout-tty)
(grub-out-display grubout-tty)
(setf (grubout-tty-to-yank) (grubout-tty-kill-buffer))
())
(defstruct (grubout-handler sfa conc-name default-pointer)
(substream (make-grubout-tty)))
(defun create-grubout-handler () (make-grubout-handler))
(defvar grubout-handler-substream () "NIL at toplevel")
(defvar scrolling-jump 4)
(defun handle-error-during-read ignore
(setf (grubout-tty-to-yank grubout-handler-substream) ())
(let ((to (grubout-tty-tyo grubout-handler-substream)))
(let* ((p (cursorpos to))
(h (- (car status-ttysize) (car p) 1)))
(cond ((and scrolling? (< h scrolling-jump))
(do ((j 0 (1+ j)))
((= j scrolling-jump))
(tyo #\LF to))
(do ((l (grubout-tty-display grubout-handler-substream) (cdr l))
(s (- scrolling-jump h)))
((null l)
(cursorpos (- (car p) s) (cdr p) to))
(rplaca (cdar l) (- (cadar l) s))))))
(errprint () to)
(cursorpos 'a to)
(do () (()) (tyi grubout-handler-substream))))
(defun grubout-handler (grubout-handler com arg)
(caseq com
((RUBOUT-HANDLER)
(let ((grubout-handler-substream (grubout-handler-substream))
(bad-tty? (and (eq 'tops-20 (status opsys))
(< (status ttytyp) 9.)))
(status-ttysize (status ttysize))
(echofiles ())
(echofiles-value echofiles))
(PROG2 (sfa-call grubout-handler-substream 'init ())
(do ()(nil)
(*catch 'grubout
(errset-bind #'handle-error-during-read
(return (funcall (car arg)
grubout-handler-substream
(caddr arg))))))
(SFA-CALL GRUBOUT-HANDLER-SUBSTREAM 'AFTER-READ (CAR ARG)))))
((which-operations)
'(RUBOUT-HANDLER TYI UNTYI TYIPEEK))
;; random other calls have to go to the substream of the substream.
(TYI
(TYI (GRUBOUT-TTY-TYI (GRUBOUT-HANDLER-SUBSTREAM)) ARG))
(UNTYI
(UNTYI ARG (GRUBOUT-TTY-TYI (GRUBOUT-HANDLER-SUBSTREAM))))
(TYIPEEK
(TYIPEEK () (GRUBOUT-TTY-TYI (GRUBOUT-HANDLER-SUBSTREAM))))))
(DECLARE (SPECIAL GRUBOUT-handler))
(setq grubout-handler (create-grubout-handler))
(defun whitespacep/.code-char (x)
(member x '(#\sp #\tab #\cr #\lf #\ff)))
(sstatus ttyint #↑w ())
(defun grubout-handler-call (f)
(let ((stream grubout-handler)
(grubout-handler ()))
;; just in case there are calls to the rubout handler
;; while we are reading something else.
(or stream (setq stream (create-grubout-handler)))
(sfa-call stream 'rubout-handler (list f stream))))
(defun tty-read (&optional (read-prompt ""))
(grubout-handler-call 'read))
(defun tty-readline (&optional (read-prompt ""))
(grubout-handler-call 'readline))
(defun tty-tyi (&optional (read-prompt ""))
(grubout-handler-call 'tyi))
(defun tty-redisplay (&optional (message ""))
(cond (grubout-handler-substream
(cursorpos 'a (grubout-tty-tyo grubout-handler-substream))
(princ message (grubout-tty-tyo grubout-handler-substream))
(re-display grubout-handler-substream))
('else
(tty-printline message))))
(defun tty-printline (&rest l)
(do ()
((NULL L))
(cursorpos 'a tyo)
(princ (pop l) tyo)
(terpri tyo)))